﻿using System;
using DbUpgrader.Apis.Db;
using DbUpgrader.Apis.HashComputers;

namespace DbUpgrader.Core
{
    public class DBUpgrader
    {
        private const string CreateTableChangelog = "CREATE TABLE IF NOT EXISTS ChangeLog (" +
                                                    "Id INTEGER PRIMARY KEY, " +
                                                    "Number INTEGER, " +
                                                    "HashRequest char(50));";

        private const string SelectHashRequest = "SELECT HashRequest FROM ChangeLog WHERE Number = '{0}';";
        private const string RequestExists = "SELECT COUNT(Id) FROM ChangeLog WHERE Number = '{0}';";
        private const string InsertHashRequest = "INSERT INTO ChangeLog VALUES (NULL, '{0}', '{1}')";

        private IDb db;
        private IHashComputer hashComputer;

        public DBUpgrader(IDb db, IHashComputer hashComputer)
        {
            this.db = db;
            this.hashComputer = hashComputer;
        }

        public void Upgrade(params string[] requests)
        {
            CreateTableChangelogIfNecessary();
            for (int index = 0; index < requests.Length; index++)
            {
                string request = requests[index];
                if (RequestAlreadyExecuted(index))
                {
                    if (!IsSameRequest(index, request))
                        throw new DbUpgradeException(request);
                    continue;
                }
                ExecuteRequest(request);
                MarkRequestAsExecuted(index, request);
            }
        }

        private void ExecuteRequest(string request)
        {
            db.ExecuteRequest(request);
        }

        private void CreateTableChangelogIfNecessary()
        {
            db.ExecuteRequest(CreateTableChangelog);
        }

        private void MarkRequestAsExecuted(int index, string request)
        {
            string hash = hashComputer.ComputeHash(request);
            db.ExecuteRequest(String.Format(InsertHashRequest, index, hash));
        }

        private bool RequestAlreadyExecuted(int index)
        {
            return db.ExecuteQuery<bool>(String.Format(RequestExists, index));
        }

        private bool IsSameRequest(int index, string request)
        {
            string hash = hashComputer.ComputeHash(request);
            return db.ExecuteQuery<string>(String.Format(SelectHashRequest, index)) == hash;
        }
    }
}